﻿using RealDataReceiver.Entities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Framework.Business;
using Framework.Helper;
using MongoDB.Driver;
using Newtonsoft.Json;
using RealDataReceiver.DataAccess;
using ServiceStack.Text;

namespace RealDataReceiver.Business
{
    public class AlarmReceiveBusiness
    {
        public static void AlarmReceiveProcess(ServiceContextBase<AlarmReceiverRequest, AlarmReceiverResponse> ctx)
        {
            bool recordFile = ConfigurationHelper.GetBoolean("EnableAlarmRecordFile", false);
            ctx.Log(string.Format("Enable Alarm info record file [{0}]", recordFile));

            if (recordFile)
            {
                var formatMsg = ConverteRawDataToAlarmMetaDatas(ctx);

                var folderName = ConfigurationHelper.GetConfiguration("DataFolder");
                var fileName = ConfigurationHelper.GetConfiguration("AlarmFileName");

                if (!Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, folderName)))
                {
                    Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, folderName));
                }
                var fullPath = Path.Combine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, folderName), fileName);
                if (!Directory.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, folderName, "RawString")))
                {
                    Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, folderName, "RawString"));
                }
                var rawDataFullPath = Path.Combine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, folderName,"RawString"), fileName);

                WriteToFile(formatMsg,ctx.RawInputString, fullPath, rawDataFullPath);

                return;
            }

            if (ctx.Request.data.IsNullOrEmpty())
            {
                ctx.Warn("No alarm data in request.");
                return;
            }

            RecordNosql(ctx);
        }

        public static void RetrieveRealData(ServiceContextBase<AlarmQueryRequest, AlarmQueryResponse> ctx)
        {
            ctx.Log("Retrieve data from nosql.");
            //var dataAccess = new AlarmDA();
            var datas = AlarmDA.Instance.GetCollection().AsQueryable(new AggregateOptions { AllowDiskUse = true });

            var result = datas.Where(e => e.Status == "A")
                .OrderByDescending(t => t.UpdateTime)
                .GroupBy(r => r.DeviceId).Select(g => new AlarmMetaData()
            {
                Id = g.First().Id,
                Key = g.First().Key,
                LocationId = g.First().LocationId,
                LocationName = g.First().LocationName,
                DeviceId = g.First().DeviceId,
                DeviceName = g.First().DeviceName,
                PropertyId = g.First().PropertyId,
                PropertyName = g.First().PropertyName,
                UpdateTime = g.First().UpdateTime,
                Level = g.First().Level,
                AlarmType = g.First().AlarmType,
                Description = g.First().Description,
                AlarmId = g.First().AlarmId,
                IP = g.First().IP
            }).ToList();

            ctx.Response.Result = result;
            ctx.Response.Count = result.Count;
        }

        private static void RecordNosql(ServiceContextBase<AlarmReceiverRequest, AlarmReceiverResponse> ctx)
        {
            var data = ConverteRawDataToAlarmMetaDatas(ctx);

            FormatDeviceMetaData(data);

            //AlarmDA dataAccess = new AlarmDA();
            ctx.Debug(">>> Start to record cloud.");
            AlarmDA.Instance.SaveMany(data);
            ctx.Log("<<< Record successfully.");

        }

        private static void FormatDeviceMetaData(List<AlarmMetaData> alarmMetaDatas)
        {
            if (alarmMetaDatas.IsNullOrEmpty())
                return;

            var allChannels = ChannelDA.Instance.GetAllChannels();
            if (allChannels == null)
                return;

            foreach (var alarmMetaData in alarmMetaDatas)
            {
                var ids = alarmMetaData.DeviceId.Split('.');
                if(ids.Length <=0 )
                    continue;
                var locationId = ids[0];
                var deviceId = "";
                if (ids.Length >= 2)
                    deviceId = ids[1];
                var propertyId = "";
                if (ids.Length >= 3)
                    propertyId = ids[2];
                var localChannel = allChannels.FirstOrDefault(l => l.ChannelId == locationId);
                if (localChannel == null)
                    continue;
                alarmMetaData.LocationId = locationId;
                alarmMetaData.LocationName = localChannel.ChannelName;
                var device = localChannel.DeviceList.FirstOrDefault(d => d.DeviceId == deviceId);
                if (device == null)
                    continue;
                alarmMetaData.DeviceId = deviceId;
                alarmMetaData.DeviceName = device.DeviceName;
                var signal = device.SignalList.FirstOrDefault(s => s.SignalId == propertyId);
                if (signal == null)
                    continue;
                alarmMetaData.PropertyId = propertyId;
                alarmMetaData.DeviceName = signal.SignalName;
            }
        }

        public static List<AlarmMetaData> ConverteRawDataToAlarmMetaDatas(ServiceContextBase<AlarmReceiverRequest, AlarmReceiverResponse> ctx)
        {
            List<AlarmMetaData> result = new List<AlarmMetaData>();

            foreach (var rawData in ctx.Request.data)
            {
                AlarmMetaData metaData = new AlarmMetaData
                {
                    AlarmId = rawData.alarmId,
                    AlarmType = rawData.alarmType,
                    DeviceId = rawData.deviceId,
                    Description = rawData.description,
                    Level = rawData.level,
                    IP = rawData.devIp,
                    UpdateTime = rawData.time,
                    Status = "A"
                };
                result.Add(metaData);
            }

            return result;
        }

        public static void DeleteAlarm(ServiceContextBase<AlarmHanleRequest, AlarmHanleResponse> ctx)
        {
            AlarmDA.Instance.Update(ctx.Request.id,"D");

            ctx.Log($"Success update alarm id [{ctx.Request.id}] status to D");
        }

        private static void WriteToFile(List<AlarmMetaData> msg,string rawString, string filePath, string rawDataPath)
        {
            var jsonString = JsonConvert.SerializeObject(msg);
            byte[] textByte = Encoding.UTF8.GetBytes(jsonString);
            using (FileStream strm = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Write))
            {
                strm.Write(textByte, 0, textByte.Length);
            }

            byte[] textRawByte = Encoding.UTF8.GetBytes(rawString);
            using (FileStream strm = new FileStream(rawDataPath, FileMode.Create, FileAccess.Write, FileShare.Write))
            {
                strm.Write(textRawByte, 0, textRawByte.Length);
            }
        }
    }
}
